/** \file gr_shims.h Adapter template fucntions to make using different container types uniform. */
#pragma once

#include<memory>
#include<functional>
#include<iterator>
#include<algorithm>
#include<string>
#include<sstream>

/* Macros to abbreviate using switch-cases for polymorphism. */
#define TYPE_SWITCH_START(type_param) switch type_param {
#define TYPE_SWITHC_END(type_param, default_action) default: default_action break;}
#define TYPE_CASE(type_param, action_param) case type_param: action_param  break;

namespace gr
{

template<class V>
std::string toString(V& var)
{
    std::ostringstream stream;
    stream << var;
    return stream.str();
}

/** Test if collection is empty. */
template<class T>
bool isEmpty(const T& collection){return collection.empty();}

/** Class template that is used to wrap specializations of areEqual. */
template<class T> struct AreEqualFun;

/** Implement boolean equal test for supported types */
template<class T>
bool areEqual(const T& first, const T& second){return AreEqualFun<T>::eq(first, second);}

/** Return size of container. */
template<class V>
inline size_t size(V& container)
{
    return container.size();
}

/** Get last addressable index of container. */
template<class V>
inline size_t lastIndex(V& container)
{
    return container.size() - 1;
}

/** Insert data to container */
template<class V>
void insert(V& container, typename V::value_type& data)
{
    *std::back_inserter(container) = data;
}

/** Returns a pointer to the element holding the specified value or null */
template<class V>
typename V::value_type* find(V& container, const typename V::value_type& ref)
{
    auto i = std::find(container.begin(), container.end(), ref);
    if(i == container.end()) return nullptr;
    else return &(*i);
}

/** Returns a pointer to the element holding the specified value or null */
template<class V>
typename V::value_type* find(V& container, std::function<bool(const typename V::value_type&)> cmp)
{
    auto i = std::find_if(container.begin(), container.end(), cmp);
    if(i == container.end()) return nullptr;
    else return &(*i);
}

template<class V>
class Insert
{
public:
    typedef std::back_insert_iterator<V> iterator;
    iterator inserter_;

    Insert(V& container):inserter_(std::back_inserter(container)){}
    Insert(iterator i):inserter_(i){}
    Insert& operator()(typename V::value_type& data)
    {
        *inserter_ = data;
        inserter_++;
        return *this;
        //return Insert(inserter_++);
    }
    Insert& operator()(const V& source)
    {
        std::copy(source.begin(), source.end(), inserter_);
        return *this;
    }
};

template<class V>
Insert<V> insert(V& container){return Insert<V>(container);} 

/* Implementations for our template utilities. */

template<class T> struct AreEqualFun
{
    static bool eq(const T& first, const T& second){return first == second;}
};
template<> struct AreEqualFun <std::string>
{
    static bool eq(const std::string& first, const std::string& second){return first.compare(second) == 0;}
};

}
